package com.wwg.util;

import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import sun.reflect.generics.reflectiveObjects.TypeVariableImpl;

import java.lang.reflect.*;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;

/**
 * 云路供应链科技有限公司 版权所有 © Copyright 2019<br>
 *
 * @Description: 生成前端所需文档<br>
 * @Project: <br>
 * @CreateDate: Created in 2022/6/22 16:02 <br>
 * @Author: <a href="huangyangbing@jtexpress.com">yangbing.huang</a>
 */
public class GetDocumentUtil {
    private final static String URL_PREFIX = "/networkmanagement";

    public static void main(String[] args) throws ClassNotFoundException {
        System.out.println(exportSwagger());
    }

    private static String exportSwagger() throws ClassNotFoundException {
        StringBuilder result = new StringBuilder("");
        Class a = Class.forName("com.yl.networkmanagement.oms.controller.OmsWaybillController");
        RequestMapping requestMapping = (RequestMapping) a.getAnnotation(RequestMapping.class);
        String urlPrefix = URL_PREFIX + requestMapping.value()[0];
        Method[] methods = a.getMethods();

        for (Method method : methods) {
            RequestObject requestObject = getRequestObject(method);
            if (requestObject != null) {

                ApiOperation operation = method.getAnnotation(ApiOperation.class);
                //组装request方法体
                result.append("url:").append(urlPrefix).append(requestObject.getUrlPath()).append("\n")
                        .append("desc:").append( operation != null ? operation.value() :requestObject.getDesc()).append("\n")
                        .append("method: ").append(requestObject.getMethodName()).append("\n");

                StringBuilder params = new StringBuilder();
                StringBuilder body = new StringBuilder();

                //获取入参和body
                getParamAndBody(method,params,body);

                //组装body参数
                result.append("param:\n").append(params).append("\n")
                        .append("body:\n")
                        .append(body);

                //组装返参
                result.append("return:\n")
                        .append(getParameter(method.getGenericReturnType(), 1)).append("\n");
            }


        }
        return result.toString();
    }

    private static void getParamAndBody(Method method,StringBuilder params,StringBuilder body){

        Parameter[] parameters= method.getParameters();
        for(int i =0;i<parameters.length;i++){
            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
            if(requestBody !=null){
                body.append(getParameter(parameters[i].getParameterizedType(),1)).append("\n");
            }else if(requestParam !=null){
                String paramValue =requestParam.value();
                params.append("\t").append(StringUtils.isEmpty(paramValue)?parameters[i].getName():paramValue).append("\t").append(getParameter(parameters[i].getParameterizedType(),1)).append("\n");
            }

        }
    }

    /**
     * 获取请求方法，地址，和信息
     * @param method
     * @return
     */
    private static RequestObject getRequestObject(Method method) {
        PostMapping postMapping = method.getAnnotation(PostMapping.class);
        GetMapping getMapping = method.getAnnotation(GetMapping.class);
        RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
        RequestObject requestObject = new RequestObject();
        StringBuilder urlSb = new StringBuilder();
        if (postMapping != null) {
            requestObject.setMethodName("post");
            for (String url: postMapping.value()){
                urlSb.append(url).append("||");
            }
            requestObject.setUrlPath(urlSb.substring(0, urlSb.length() - 2));
            requestObject.setDesc(postMapping.name());
            return requestObject;
        } else if (getMapping != null) {
            requestObject.setMethodName("get");
            for (String url: getMapping.value()){
                urlSb.append(url).append("||");
            }
            requestObject.setUrlPath(urlSb.substring(0, urlSb.length() - 2));
            requestObject.setDesc(getMapping.name());
            return requestObject;
        } else if (requestMapping != null) {
            StringBuilder sb = new StringBuilder();
            for (RequestMethod requestMethod : requestMapping.method()) {
                sb.append(requestMethod.name()).append("||");
            }
            requestObject.setMethodName(sb.substring(0, sb.length() - 2));
            for (String url: requestMapping.value()){
                urlSb.append(url).append("||");
            }
            requestObject.setUrlPath(urlSb.substring(0, urlSb.length() - 2));
            requestObject.setDesc(requestMapping.name());
            return requestObject;
        }

        return null;

    }

    /**
     * 处理类型
     * @param type
     * @param i
     * @return
     */
    private static String getParameter(Type type, int i) {
        //校验本身是否为泛型
        if (type instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) type;
            //校验是否嵌套泛型
            if (pt.getActualTypeArguments()[0] instanceof Class) {
                Class rawType = (Class<?>) pt.getRawType();
                return getFieldNameValueForClazz(rawType, i, null, (Class<?>) pt.getActualTypeArguments()[0]);
            } else if (pt.getActualTypeArguments()[0] instanceof ParameterizedType) {
                Class rawType = (Class<?>) pt.getRawType();
                return getFieldNameValueForClazz(rawType, i, (ParameterizedType) pt.getActualTypeArguments()[0], null);
            } else {

                return "未知类型：" + type.getTypeName();
            }


        } else if (type instanceof Class) {
            return getFieldNameValueForClazz((Class<?>) type, i, null, null);
        } else {
            return "未知类型：" + type.getTypeName();
        }


    }

    /**
     * 获取字段名称，属性，及 含义
     * @param clazz
     * @param i
     * @param nextType
     * @param nextClass
     * @return
     */
    private static String getFieldNameValueForClazz(Class clazz, int i, ParameterizedType nextType, Class<?> nextClass) {
        StringBuilder result = new StringBuilder();
        //如果为非复合类型，直接返回
        String simpleName = getSimpleName(clazz);
        String prefix = String.join("", Collections.nCopies(i, "\t"));
        if(simpleName!=null){
            return prefix+simpleName;
        }

        if (clazz.equals(List.class)) {
            String nextClassSimpleName = getSimpleName(nextClass);
            if(nextClassSimpleName !=null){
                result.append(prefix).append("List<").append(nextClassSimpleName).append(">");
            }else{
                result.append(prefix).append("List<Object>:\n").append(getParameter(nextClass, i + 1));
            }

            return result.toString();
        }

        Field[] fields = clazz.getDeclaredFields();


        for (Field field : fields) {
            if ("serialVersionUID".equals(field.getName())) {
                continue;
            }
            field.setAccessible(true);

            Type type = field.getGenericType();
            result.append(prefix).append(field.getName());
            boolean base = false;
            String simpleFieldName = getSimpleName(type);
            if(simpleFieldName !=null){
                result.append("\t").append(simpleFieldName).append("\t");
                base = true;
            } else{
                if (type instanceof ParameterizedType) {
                    ParameterizedType pt = (ParameterizedType) type;

                    if(pt.getRawType().equals(List.class)){
                        String simpleTypeName = getSimpleName(pt.getActualTypeArguments()[0]);
                        result.append("\t").append("List<").append(simpleTypeName==null?"object":simpleTypeName).append(">").append("\t");
                        if(simpleTypeName!=null){
                            base=true;
                        }

                    }else if (pt.getActualTypeArguments()[0] instanceof Class) {

                        result.append("\t").append(((Class<?>) pt.getActualTypeArguments()[0]).getSimpleName()).append("\t");
                    } else if (pt.getActualTypeArguments()[0] instanceof ParameterizedType) {

                        result.append("\t").append(((ParameterizedType) pt.getActualTypeArguments()[0]).getRawType().getTypeName()).append("\t");
                    } else {

                        result.append("\t").append("object").append("\t");
                    }
                } else if (type instanceof Class) {
                    result.append("\t").append(((Class<?>) type).getSimpleName()).append("\t");
                }else if(type instanceof TypeVariableImpl){
                    if (nextType != null) {
                        if(nextType.getRawType().equals(List.class)){
                            String simpleTypeName = getSimpleName(nextType.getActualTypeArguments()[0]);
                            result.append("\t").append("List<").append(simpleTypeName==null?"object":simpleTypeName).append(">").append("\t");
                            if(simpleTypeName!=null){
                                base=true;
                            }
                        }

                    }else{
                        result.append("\t").append("object").append("\t");
                    }


                }
            }


            String mean = getMean(field);
            if (mean != null) {
                result.append(" ").append(mean);
            }
            result.append("\n");
            if (!base) {
                if (type == null) {
                    continue;
                }
                String tt = null;
                if (type instanceof ParameterizedType) {
                    ParameterizedType pt = (ParameterizedType) type;
                    if (pt.getActualTypeArguments()[0] instanceof Class) {
                        Class<?> actualTypeArgument = (Class<?>) pt.getActualTypeArguments()[0];
                        if (!actualTypeArgument.equals(Integer.class) && !actualTypeArgument.equals(String.class)) {
                            tt = getParameter(actualTypeArgument, i + 1);
                        }
                    } else if (pt.getActualTypeArguments()[0] instanceof TypeVariableImpl) {
                        if (nextType != null) {
                            tt = getParameter(nextType, i + 1);
                        } else if (nextClass != null) {
                            tt = getParameter(nextClass, i + 1);
                        }

                    }


                } else if (type instanceof TypeVariableImpl) {
                    if (nextType != null) {
                        if(nextType.getRawType().equals(List.class)){
                            tt = getParameter(nextType.getActualTypeArguments()[0],i+1);
                        }else{
                            tt = getParameter(nextType, i + 1);
                        }

                    } else if (nextClass != null) {
                        tt = getParameter(nextClass, i + 1);
                    }
                } else if (type instanceof Class) {
                    tt = getParameter(type, i + 1);
                }
                if (tt != null) {
                    result.append(tt);
                }


            }


        }
        return result.toString();


    }


    private static String getMean(Field field) {
        ApiModelProperty fieldAnnotation = field.getAnnotation(ApiModelProperty.class);
        if (fieldAnnotation != null) {
            if (!StringUtils.isEmpty(fieldAnnotation.name())) {
                return fieldAnnotation.name();
            } else if (!StringUtils.isEmpty(fieldAnnotation.value())) {
                return fieldAnnotation.value();
            }
        }

        return null;
    }


    private static String getSimpleName(Class clazz){
        if (clazz.equals(LocalDateTime.class) ||
                clazz.equals(LocalDate.class) ||
                clazz.equals(String.class) ||
                clazz.equals(Long.class) ||
                clazz.equals(Boolean.class)||
                clazz.equals(BigDecimal.class) ||
                clazz.equals(Integer.class)  || clazz.isPrimitive()) {
            return clazz.getSimpleName();
        }else {
            return null;
        }
    }

    private static String getSimpleName(Type type){
        if ( type instanceof Class) {
            return getSimpleName((Class<?>) type);
        }else {
            return null;
        }
    }


    static class RequestObject {
        String methodName;
        String urlPath;
        String desc;

        RequestObject() {

        }

        public String getMethodName() {
            return methodName;
        }

        public void setMethodName(String methodName) {
            this.methodName = methodName;
        }

        public String getUrlPath() {
            return urlPath;
        }

        public void setUrlPath(String urlPath) {
            this.urlPath = urlPath;
        }

        public String getDesc() {
            return desc;
        }

        public void setDesc(String desc) {
            this.desc = desc;
        }
    }


}

